﻿//////////////////////////////////////////////////////////////////////////////////////
//
//
//               程    序：王盛
//
//               创建时间：2010.08.03
//
//               修订时间：
//////////////////////////////////////////////////////////////////////////////////////



using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Walson.IO;


namespace Walson.Net.UdpClientCompact
{
    /// <summary>
    /// 带有环形缓冲区的精简 UdpClient
    /// </summary>
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(UdpClientCompact))]
    [DefaultEvent("DataReceived")]
    public class UdpClientCompact : Component
    {
        // 输入数据流缓冲区。该数据流为一环形流，来自 Socket 的数据将写入此流中
        private RingFIFOMemoryStream BufferStream;

        // 接收线程启动成功指示
        private bool m_bReceiveThreadStartOK = false;

        // 终止UDP接收线程指示，当 DSRU 销毁时应当将其置为 true
        private bool m_bTerminateReciever = false;

        private object locker;

        // 缓冲区地图
        ArrayList m_arlBufferMap = new ArrayList();

        // 发消息的源的 IP 地址
        ArrayList m_arlRemoteIPEndPoint = new ArrayList();

        // 发消息的源的端口号
        ArrayList m_arlRemotePort = new ArrayList();

        private Thread m_thdUdpData;

        // IP 地址
        private string m_strIpAddress;

        // 端口号
        private int m_intPort;

        /// <summary>
        /// 名称
        /// </summary>
        private string m_strName;

        /// <summary>
        /// 缓冲区大小
        /// </summary>
        private int m_intBufferSize;

        private Socket m_socketDataTraneceiver;

        /// <summary>
        /// 指示是否在进行清除缓冲区的操作
        /// </summary>
        private bool m_bClearing=false;


        /// <summary>
        /// 获取或设置用于数据收发的IP地址
        /// </summary>
        public string Address
        {
            set { m_strIpAddress = value; }
            get { return m_strIpAddress; }
        }

        /// <summary>
        /// 获取或设置用于数据收发的端口
        /// </summary>
        public int Port
        {
            set { m_intPort = value; }
            get { return m_intPort; }
        }

        /// <summary>
        /// 获取或设置组件的名称
        /// </summary>
        public string Name
        {
            set { m_strName = value; }
            get { return m_strName; }
        }

        /// <summary>
        /// 获取组件运行状态
        /// </summary>
        public bool IsRunning
        {
            get { return m_bReceiveThreadStartOK; }
        }

        /// <summary>
        /// 设置或获取缓冲区大小
        /// </summary>
        public int BufferSize
        {
            set { m_intBufferSize = value; }
            get { return m_intBufferSize; }
        }

        /// <summary>
        /// 流中的可用数据长度
        /// </summary>
        public int Available
        {
            //get { return BufferStream.Length; }
            get { return m_arlBufferMap.Count; }
        }

        /// <summary>
        /// 事件到达处理方法
        /// </summary>
        /// <param name="sender">事件的发送方</param>
        /// <param name="e">参数</param>
        public delegate void DataReceivedHandler(object sender, EventArgs e);
        /// <summary>
        /// 数据到达事件
        /// </summary>
        public event DataReceivedHandler DataReceived;



        /// <summary>
        /// 原始消息的相关信息，包含原始消息字节数组和发消息的源的 IP 地址
        /// </summary>
        public class MessageInfo
        {
            /// <summary>
            /// 消息体
            /// </summary>
            public byte[] MessageBody;
            /// <summary>
            /// 发送方信息
            /// </summary>
            public IPEndPoint SenderInfo;
            /// <summary>
            /// 原始消息的相关信息
            /// </summary>
            /// <param name="RemoteIPEndpoint"></param>
            public MessageInfo(IPEndPoint RemoteIPEndpoint)
            {
                MessageBody = null;
                SenderInfo = RemoteIPEndpoint;
            }
        }

        /// <summary>
        /// 停止 UdpClientCompact
        /// </summary>
        public void Stop()
        {
            m_bTerminateReciever = true;
            int timeout = 0;
            while (m_thdUdpData.IsAlive)
            {
                m_bTerminateReciever = true;
                m_thdUdpData.Join(1000);
                timeout++;
                if (m_thdUdpData.IsAlive == false)
                    break;
                if (timeout == 5)
                    throw new Exception("UdpClientCompact 停止失败！");
            }
        }

        /// <summary>
        /// 清除缓冲区内数据
        /// </summary>
        public void Clear()
        {
            m_bClearing = true;
            lock (locker)
            {
                int length = 0;
                foreach (int o in m_arlBufferMap)
                {
                    length += (int)o;
                }
                BufferStream.Read(new byte[length], 0, length);
                m_arlBufferMap.Clear();
                m_arlRemoteIPEndPoint.Clear();
            }
            m_bClearing = false;
        }

        /// <summary>
        /// 创建带环形缓冲流的精简UdpClient，用于收发UDP数据报
        /// </summary>
        /// <param name="IpAddress">用于收发数据的IP地址</param>
        /// <param name="Port">用于收发数据的端口</param>
        /// <param name="BufferSize">缓冲区大小</param>
        public UdpClientCompact(string IpAddress, int Port, int bufferSize)
        {
            m_strIpAddress = IpAddress;
            m_intPort = Port;
            m_intBufferSize = bufferSize;
            locker = new object();
            BufferStream = new RingFIFOMemoryStream();
        }

        /// <summary>
        /// 使用默认缓冲区尺寸创建带环形缓冲流的精简UdpClient，用于收发UDP数据报
        /// </summary>
        /// <param name="IpAddress">用于收发数据的IP地址</param>
        /// <param name="Port">用于收发数据的端口</param>
        public UdpClientCompact(string IpAddress, int Port)
            : this(IpAddress, Port, 102400)
        {

        }

        /// <summary>
        /// 使用本机 IP 地址创建带环形缓冲流的精简UdpClient，用于收发UDP数据报
        /// </summary>
        /// <param name="port">端口号</param>
        /// <param name="bufferSize">缓冲区大小</param>
        public UdpClientCompact(int port,int bufferSize)
        {
            IPAddress[] addr = System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName());
            string IpAddress = "127.0.0.1";


            foreach (IPAddress address in addr)
            {
                if (address.AddressFamily == AddressFamily.InterNetwork)
                {
                    IpAddress = address.ToString();
                    break;
                }
            }


            m_strIpAddress = IpAddress;
            m_intPort = port;
            m_intBufferSize = bufferSize;
            locker = new object();
            BufferStream = new RingFIFOMemoryStream();
        }

        /// <summary>
        /// 使用默认缓冲区尺寸创建带环形缓冲流的精简UdpClient，用于收发UDP数据报
        /// </summary>
        /// <param name="port">端口号</param>
        public UdpClientCompact(int port)
            : this(port, 102400)
        {

        }

        /// <summary>
        /// 使用默认缓冲区尺寸和端口号创建带环形缓冲流的精简UdpClient，用于收发UDP数据报
        /// </summary>
        public UdpClientCompact()
            :this(9000)
        {

        }



        /// <summary>
        /// 
        /// </summary>
        ~UdpClientCompact()
        {
            if (m_socketDataTraneceiver==null)
                return;
            if (m_socketDataTraneceiver.Connected)
                m_socketDataTraneceiver.Disconnect(true);
        }

        /// <summary>
        /// 启动 UdpClientCompact
        /// </summary>
        public void Start()
        {
            this.ReStartUdp();
            Thread.Sleep(200);
        }

        /// <summary>
        /// 启动/重新启动UDP
        /// </summary>
        private void ReStartUdp()
        {
            if (m_thdUdpData != null)
            {
                Stop();
            }
            
            m_thdUdpData = new Thread(new ThreadStart(thdUdpReceiver));
            m_thdUdpData.Priority = ThreadPriority.Highest;
            m_thdUdpData.IsBackground = true;
            m_thdUdpData.Start();
            
        }

        /// <summary>
        /// UDP数据接收线程，负责从 Socket 接收 UDP 数据包并写入环形FIFO流中
        /// </summary>
        private void thdUdpReceiver()
        {

            EndPoint m_endpRemoteEndPoint;
            IPEndPoint m_localEndPoint;
            int m_intReceivedBytes = 0;

            byte[] m_byteRawDgram;
            m_bReceiveThreadStartOK = false;
            m_bTerminateReciever = false;
            try
            {
                m_endpRemoteEndPoint = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));
                m_localEndPoint = new IPEndPoint(IPAddress.Parse(m_strIpAddress), m_intPort);
                m_socketDataTraneceiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                m_socketDataTraneceiver.Bind(m_localEndPoint);
                //设置Socket接收缓存为 Consts.DSRUBufferSize 字节
                m_socketDataTraneceiver.ReceiveBufferSize = m_intBufferSize;
            }
            catch (Exception ex)
            {
                throw (new Exception("UdpClientCompact启动失败。请检查IP地址、端口号及协议版本（IPv4/IPv6）是否正确。本组件仅支持IPv4。\r\n" + ex.Message));
            }

            m_bReceiveThreadStartOK = true;

            // 清除接收缓存
            while (m_socketDataTraneceiver.Available != 0)
            {
                m_byteRawDgram = new byte[m_socketDataTraneceiver.Available];
                m_intReceivedBytes = m_socketDataTraneceiver.ReceiveFrom(m_byteRawDgram, ref m_endpRemoteEndPoint);
            }

            while (m_bTerminateReciever == false)
            {
                if (m_socketDataTraneceiver.Available == 0)
                {
                    Thread.Sleep(1);
                    continue;
                }
                m_byteRawDgram = new byte[m_socketDataTraneceiver.Available];
                try
                {
                    m_intReceivedBytes = m_socketDataTraneceiver.ReceiveFrom(m_byteRawDgram, ref m_endpRemoteEndPoint);
                }
                catch(Exception )
                {
                    continue;
                }
                
                // 如果收到的数据长度为0，则表明数据有问题，应当丢弃
                if (m_intReceivedBytes == 0)
                    continue;
                if(m_bClearing)
                    continue;

                lock (locker)
                {
                    // 将当前收到的数据长度写入缓冲区地图中
                    m_arlBufferMap.Add(m_intReceivedBytes);
                    // 将发送当前消息的源的 IP 地址记录下来
                    m_arlRemoteIPEndPoint.Add((IPEndPoint)m_endpRemoteEndPoint);
                    
                    BufferStream.Write(m_byteRawDgram, 0, m_intReceivedBytes);
                }

                if (DataReceived != null)
                    DataReceived(this, new EventArgs());
            }
            m_socketDataTraneceiver.Close();
            m_bReceiveThreadStartOK = false;
        }

        /// <summary>
        /// 向指定位置发送数据
        /// </summary>
        /// <param name="ipAddress">目的 IP 地址</param>
        /// <param name="port">目的端口号</param>
        /// <param name="payload">要发送的数据</param>
        /// <param name="length">数据长度</param>
        /// <returns>发送的长度</returns>
        public int SendTo(string ipAddress, int port, Byte[] payload, int length)
        {
            if (m_socketDataTraneceiver == null)
                throw new Exception("请先使用 Start() 方法启动组件。");

            return m_socketDataTraneceiver.SendTo(payload, length, SocketFlags.None, (EndPoint)(new IPEndPoint(IPAddress.Parse(ipAddress), port)));
        }

        /// <summary>
        /// 向指定位置发送数据
        /// </summary>
        /// <param name="ipAddress">目的 IP 地址</param>
        /// <param name="port">目的端口号</param>
        /// <param name="payload">要发送的数据</param>
        /// <returns>发送的长度</returns>
        public int SendTo(string ipAddress, int port, Byte[] payload)
        {
            return SendTo(ipAddress, port, payload, payload.Length);
        }

        /// <summary>
        /// 向指定位置发送数据
        /// </summary>
        /// <param name="ipAddress">目的 IP 地址</param>
        /// <param name="port">目的端口号</param>
        /// <param name="payload">要发送的数据</param>
        /// <returns>发送的长度</returns>
        public int SendTo(string ipAddress, int port, string str)
        {
            return SendTo(ipAddress, port, Encoding.Default.GetBytes(str));
        }

        /// <summary>
        /// 向指定位置发送数据
        /// </summary>
        /// <param name="ipAddress">目的 IP 地址</param>
        /// <param name="port">目的端口号</param>
        /// <param name="structOrClass">要发送的数据，可以是类或结构</param>
        /// <returns>发送的长度</returns>
        public int SendTo(string ipAddress, int port, object structOrClass)
        {
            byte[] payload = Walson.Tools.Converter.StructToBytes(structOrClass);
            return SendTo(ipAddress, port, payload, payload.Length);
        }

        /// <summary>
        /// 向指定位置发送广播数据
        /// </summary>
        /// <param name="port">目的端口号</param>
        /// <param name="payload">要发送的数据</param>
        /// <param name="length">要发送的数据长度</param>
        /// <returns>发送的长度</returns>
        public int Broadcast(int port, byte[] payload, int length)
        {
            if (m_socketDataTraneceiver == null)
                throw new Exception("请先使用 Start() 方法启动组件。");


            string[] str = this.Address.Split('.');
            IPAddress addr = IPAddress.Parse(string.Format("{0}.{1}.{2}.255", str[0], str[1], str[2]));

            return m_socketDataTraneceiver.SendTo(payload, length, SocketFlags.None, (EndPoint)(new IPEndPoint(addr, Port)));
        }

        /// <summary>
        /// 向指定位置发送广播数据
        /// </summary>
        /// <param name="port">目的端口号</param>
        /// <param name="payload">要发送的数据</param>
        /// <returns>发送的长度</returns>
        public int Broadcast(int port, byte[] payload)
        {
            return Broadcast(port, payload, payload.Length);
        }

        /// <summary>
        /// 向指定位置发送广播数据
        /// </summary>
        /// <param name="port">目的端口号</param>
        /// <param name="str">要发送的字符串</param>
        /// <returns>发送的长度</returns>
        public int Broadcast(int port, string str)
        {
            return Broadcast(port, Encoding.Default.GetBytes(str));
        }

        /// <summary>
        /// 向指定位置发送广播数据
        /// </summary>
        /// <param name="port">目的端口号</param>
        /// <param name="structObj">要发送的结构或类</param>
        /// <returns>发送的长度</returns>
        public int Broadcast(int port, object structObj)
        {
            return Broadcast(port, Walson.Tools.Converter.StructToBytes(structObj));
        }

        /// <summary>
        /// 从缓冲区中读取消息
        /// </summary>
        /// <returns>读取的消息</returns>
        public MessageInfo ReadMessage()
        {
            MessageInfo msg;
            int Length = (int)m_arlBufferMap[0];
            byte[] temp = new byte[Length];
            lock (locker)
            {
                BufferStream.Read(temp, 0, Length);
                m_arlBufferMap.RemoveAt(0);
                msg = new MessageInfo(((IPEndPoint)m_arlRemoteIPEndPoint[0]));
                msg.MessageBody = temp;
                m_arlRemoteIPEndPoint.RemoveAt(0);
                return msg;
            }
        }

        /// <summary>
        /// 销毁 UdpClientCompact
        /// </summary>
        public new void Dispose()
        {
            Stop();
            base.Dispose(true);
        }
    }

    /// <summary>
    /// UDP 广播
    /// </summary>
    public class UdpBroadcaset
    {
        // IP 地址
        private string m_strIpAddress;

        // 端口号
        private int m_intPort;

        private Socket m_socketDataReceiver;

        EndPoint m_endpRemoteEndPoint;

        IPEndPoint m_localEndPoint;

        /// <summary>
        /// UdpBroadcaset
        /// </summary>
        /// <param name="IpAddress">本机 IP 地址</param>
        /// <param name="Port">端口号</param>
        public UdpBroadcaset(string IpAddress, int Port)
        {
            m_strIpAddress = IpAddress;
            m_intPort = Port;

            try
            {
                m_localEndPoint = new IPEndPoint(IPAddress.Parse(IpAddress), m_intPort);
                m_socketDataReceiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                m_socketDataReceiver.Bind(m_localEndPoint);
                m_socketDataReceiver.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            }
            catch (Exception ex)
            {
                throw (new Exception("UdpBroadcast启动失败。请检查IP地址、端口号及协议版本（IPv4/IPv6）是否正确。本组件仅支持IPv4。\r\n" + ex.Message));
            }
        }

        /// <summary>
        /// udp广播析构
        /// </summary>
        ~UdpBroadcaset()
        {
            m_socketDataReceiver.Close();
        }

        /// <summary>
        /// 发送广播消息
        /// </summary>
        /// <param name="Port">目的端口</param>
        /// <param name="Message">消息内容</param>
        public void Send(int Port, string Message)
        {
            m_endpRemoteEndPoint = (EndPoint)(new IPEndPoint(IPAddress.Broadcast, Port));
            m_socketDataReceiver.SendTo(Encoding.Default.GetBytes(Message), m_endpRemoteEndPoint);
        }

        /// <summary>
        /// 发送广播数据
        /// </summary>
        /// <param name="Port">目的端口</param>
        /// <param name="Payload">数据内容</param>
        public void Send(int Port, byte[] Payload)
        {
            m_endpRemoteEndPoint = (EndPoint)(new IPEndPoint(IPAddress.Broadcast, Port));
            m_socketDataReceiver.SendTo(Payload, m_endpRemoteEndPoint);
        }
    }
}
